// eventhub
package eventbus

import (
	"context"
	"reflect"
)

// 提供默认的hub
var defaultTypeEventHub *TypeEventHub

func RegisterHandler(fn interface{}) {
	defaultTypeEventHub.RegisterHandler(fn)
}

// 使用反射+入参类型实现的eventhub
type TypeEventHub struct {
	// 监听映射 key入参，val函数
	handlerMap map[reflect.Type][]*typeInvoker
}

type typeInvoker struct {
	fn      interface{}   // 注册进来的方法
	fnType  reflect.Type  // 方法的类型
	fnValue reflect.Value // 方法的值
}

// 利用反射进行调用
func (invoker *typeInvoker) invoke(ctx context.Context, event interface{}) {
	var in = make([]reflect.Value, invoker.fnType.NumIn())
	in[0] = reflect.ValueOf(ctx)
	in[1] = reflect.ValueOf(event)
	invoker.fnValue.Call(in)
}

// 构造一个类型中枢
func NewTypeEventHub() *TypeEventHub {
	return &TypeEventHub{
		handlerMap: make(map[reflect.Type][]*typeInvoker),
	}
}

// 获取处理函数
func (hub *TypeEventHub) GetHandlers(event interface{}) ([]*typeInvoker, bool) {
	rtype := reflect.TypeOf(event)
	handlers, ok := hub.handlerMap[rtype]
	return handlers, ok
}

// 注册一个监听
func (hub *TypeEventHub) RegisterHandler(fn interface{}) {
	fnType := reflect.TypeOf(fn)
	if fnType.Kind() != reflect.Func {
		panic("只能注册方法")
	}
	if fnType.NumIn() != 2 {
		panic("监听方法只能有两个入参，第一个参数为上下文，第二个参数为事件")
	}
	invoker := &typeInvoker{
		fn:      fn,
		fnType:  fnType,
		fnValue: reflect.ValueOf(fn),
	}

	eventType := fnType.In(1)
	if handlers, ok := hub.handlerMap[eventType]; ok {
		hub.handlerMap[eventType] = append(handlers, invoker)
	} else {
		arr := []*typeInvoker{invoker}
		hub.handlerMap[eventType] = arr
	}
}
